home *** CD-ROM | disk | FTP | other *** search
/ The Mac 1996 October / The Mac (October 1996).dmg / Web Authoring / WebStat 2.3.4DB Folder / WebStat.cp < prev    next >
Encoding:
Text File  |  1996-01-09  |  39.8 KB  |  1,494 lines  |  [TEXT/KAHL]

  1. /*
  2. ** File:        WebStat.cp
  3. **
  4. **                A transmission statistics summary program for WinHTTP log output
  5. **                (Program originally processed MacHTTP logs - the codes still there!)
  6. **
  7. ** Author:        Philip Harvey (phil@nsun.phy.queensu.ca)
  8. **                Physics Dept
  9. **                Queen's University
  10. **
  11. ** Mods by:        Denis Birnie (D.Birnie@Regy.Canterbury.ac.NZ or WebMaster@Regy.Canterbury.ac.NZ)
  12. **                Information Services Section
  13. **                Canterbury University, New Zealand.
  14. **
  15. ** Revisions:    03/24/94 - V 1.0    - original
  16. **                03/25/94 - V 1.1    - fixed array indexing problem
  17. **                03/28/94 - V 1.2    - added anchors for lists; converted code to C++
  18. **                03/29/94 - V 1.3    - added exclusion option and application icon
  19. **                                    - changed all byte counts from long to double
  20. **                03/30/94 - V 1.4    - changed subdomain list to not reverse numerical entries
  21. **                                    - placed numerical entries at end of list
  22. **                03/31/94 - V 1.5    - added config file and moved exclusions into config
  23. **                04/04/94 - V 1.6    - added ADDRESSES Long/Short option
  24. **                                    - changed MESSAGES syntax from True/False to On/Off
  25. **                                    - added DOMAIN command to config file
  26. **                04/08/94 - V 1.7     - added comprehensive list of Domain names to config file
  27. **                                    - changed spacing to accomodate longer domain names
  28. **                04/11/94 - V 1.7.1    - delete trailing '/' if found in file names
  29. **                04/14/94 - V 1.8     - ignored improperly formatted lines in log file
  30. **                05/13/94 - V 1.9    - added SUMMARIZE options; changed to Symantec C++ 7.0
  31. **                                  - fixed quirk where days with no transfers were ignored
  32. **                06/06/94 - V 2.0    - truncate filenames at '$' because of new cgi formatting
  33. **                                    - added check on time_t values passed to GetNumDays()
  34. **                                    - added format file (replaces SUMMARIZE option of V1.9)
  35. **                08/11/94 - V 2.1    - removed limit on number of EXCLUDE and DOMAIN statements
  36. **                                    - sort numerical addresses properly
  37. **                09/27/94 - V 2.2    - added DNSLOOKUP option
  38. **                09/28/94 - V 2.3    - added progress indicator if MESSAGES Off
  39. **                                    - allows background processing if MESSAGES Off
  40. **                                    - fixed small bug in DNS lookups
  41. **                                    - Cmd-. aborts processing if MESSAGES Off
  42. **                                    - MESSAGES Off is now the default
  43. **                09/28/94 - V 2.3.1    - fixed problem in event handling during DNS lookups
  44. **                                    - fixed problem with output going to system folder broken in 2.3
  45. **                10/02/94 - V 2.3.2    - changed progress indicator; added stop button
  46. **                                    - re-fixed small bug in DNS lookups lost in 2.3.1
  47. **                10/03/94 - V 2.3.3    - fixed problem which could cause crash if stop button pressed
  48. **                                    - message dialog box is now properly activated
  49. **                10/11/94 - V 2.3.4    - progress bar now displays properly on B&W screens
  50. **                07/21/95 -            - Start of modifications by Denis Birnie
  51. **                21/07/95            - Converted Project to CodeWarrior 6 and SIOUX
  52. **                                    - Code changed to process WinHttp log files rather than MacHttp ones 
  53. **                                    - Progress Dialog message now toggles between 
  54. **                                      "Processing Log File" and "Looking up Domain Name"
  55. **                                    - Config file updated to provide more detailed domain info 
  56. **                08/24/95 - V2.3.4DB - Major changes to accomodate Visit recording
  57. **                24/08/95            - A "Visit" is a cluster of pages being sent to an IP# and then
  58. **                                      a 30 minute period with no files going to that IP#. (kVisitPause)
  59. **                                      There can of course be multiple concurrent visits. 
  60. **                                    - Made Start and End dates a little more 'robust' 
  61. **
  62. ** Suggested Improvements: Rework the DNS lookup to be asynchronous and then continue processing
  63. **                           the Log File until another unresolved IP# (other than the one currently
  64. **                           being looked up) is encountered.
  65. **
  66. ** Notes: For updates, the version number must be changed in the progress dialog title, in
  67. **          the program_name variable, and 3 times in the 'vers' resource.
  68. */
  69.  
  70. #include <ctype.h>
  71. #include <string.h>
  72. #include <stdlib.h>
  73. #include "WebStat.h"
  74. #include "TCPLib.h"
  75. #include <SIOUX.h>
  76.  
  77. enum {
  78.     kStopButtonItem        = 1,
  79.     kStaticTextItem,
  80.     kProgressIndicatorItem
  81. };
  82.  
  83. extern "C" void HandleEvents(void);
  84.  
  85. /*
  86. ** static string definitions
  87. */
  88. static char *    config_file        = "WebStat.config";
  89. static char *    def_log_file    = "MACHTTP.LOG";
  90. static char *    def_out_file    = "WebStat.html";
  91. static char *    def_fmt_file    = "WebStat.format";
  92. static char *    program_name    = "Den's WebStat for WinHttpd";
  93.  
  94. /*
  95. ** The following constants specify the number of array elements by which the
  96. ** statistics lists should grow each time more memory is required:
  97. **
  98. ** If the numbers are too small memory may become fragmented due to excess
  99. ** reallocations and the program will run more slowly, and if the numbers are
  100. ** too large more memory than necessary will be used.
  101. **
  102. ** If memory and/or speed are a concern, these numbers can be optimized for
  103. ** your individual needs.
  104. */
  105. const long        kDateIncr        = 1000;        // for date list (this will do for 3 years)
  106. const long        kHourIncr        = 24;        // for hour list (only 24 hours in a day)
  107. const long        kWDayIncr        = 7;        // for weekday list (7 days in a week)
  108. const long        kDomainIncr        = 200;        // for domain list (alot of countries)
  109. const long        kSubDomainIncr    = 5000;        // for subdomain list (alot of computers)
  110. const long        kFileIncr        = 5000;        // for archive file list (alot of files)
  111.  
  112. const long         kExcludeIncr    = 120;        // for list of exclusions (EXCLUDE statements)
  113. const long        kCountryIncr    = 600;        // for country list (DOMAIN statements)
  114. const long        kDNSListIncr    = 600;        // for DNS lookup list
  115.  
  116. const long        kVisitPause        = 30;        // number of minutes which conclude a Visit
  117. /*
  118. ** definitions of static member variables
  119. */
  120. //unsigned long    StatList::totalFiles = 0;
  121. double            StatList::totalFiles = 0;
  122. double            StatList::totalBytes = 0;
  123. double            StatList::totalVisits = 0;
  124. double            StatList::totalSites = 0;
  125.  
  126. /*-----------------------------------------------------------------------------------------
  127. ** Utility routines
  128. */
  129.  
  130. /* strcmpi - compare two strings, ignoring case, and sorting special characters last */
  131. static int strcmpi(const char *str1, const char *str2)
  132. {
  133.     char    c1,c2;
  134.     
  135.     for (;; ++str1, ++str2) {
  136.         c1 = toupper(*str1);
  137.         c2 = toupper(*str2);
  138.         if (c1==c2) {
  139.             if (c1) continue;
  140.             break;
  141.         }
  142.         if (c1 < c2) {
  143.             if (!isupper(c1) && isupper(c2)) return(1);
  144.             else return(-1);
  145.         }
  146.         if (c1 > c2) {
  147.             if (isupper(c1) && !isupper(c2)) return(-1);
  148.             else return(1);
  149.         }
  150.     }
  151.     return(0);
  152. }
  153.  
  154. /* strcmpiPart - compare two strings, ignoring case, and stopping at the end of str1 */
  155. static int strcmpiPart(const char *str1, const char *str2)
  156. {
  157.     char    c1,c2;
  158.     
  159.     for (;; ++str1, ++str2) {
  160.         c1 = toupper(*str1);
  161.         c2 = toupper(*str2);
  162.         if (!c1) break;
  163.         if (c1 < c2) return(-1);
  164.         if (c1 > c2) return(1);
  165.     }
  166.     return(0);
  167. }
  168.  
  169. /* strcmpiWild - compare two strings, ignoring case, allow wildcards */
  170. static int strcmpiWild(const char *str1, const char *str2)
  171. {
  172.     char    c1,c2;
  173.     
  174.     for (;;) {
  175.         c1 = toupper(*str1);
  176.         c2 = toupper(*str2);
  177.         if (c1=='*' || c2=='*') return(0);
  178.         if (c1 < c2) return(-1);
  179.         if (c1 > c2) return(1);
  180.         if (!c1) break;
  181.         ++str1;
  182.         ++str2;
  183.     }
  184.     return(0);
  185. }
  186.  
  187. /* strtokQuote - strtok routine with quoted strings allowed */
  188. static char *strtokQuote(char *str, char *delim)
  189. {
  190.     char        *term;
  191.     static char    *next=0;
  192.     
  193.     if (!str && ((str=next)==0 || !(*str))) return((char *)0);
  194.     
  195.     /* skip all delimiter characters */
  196.     for (; *str; ++str) if (!strchr(delim,*str)) break;
  197.     
  198.     /* are we done? */
  199.     if (!*str) return(next=(char *)0);
  200.     
  201.     /* check for start of quoted string */
  202.     if (*str == '"') {
  203.         term = strchr(++str,'"');        // terminate at matching quote
  204.     } else {
  205.         /* find next delimiter */
  206.         for (term=str+1; *term; ++term) if (strchr(delim,*term)) break;
  207.     }
  208.     
  209.     if (term && *term) {
  210.         *term = 0;            // null terminate this token
  211.         next = term + 1;
  212.     } else {
  213.         next = 0;
  214.     }
  215.         
  216.     return(str);
  217. }
  218.  
  219. /*------------------------------------------------------------------------------------------
  220. ** Mac routines
  221. */
  222.  
  223. static Rect        progressItemRect;
  224. static long        progressItemValue = -1;
  225. static Boolean    macMode = FALSE;
  226. static Boolean    wne_impl;
  227. static Rect        dragRect;
  228.  
  229.  
  230. static void ToolBoxInit(void)
  231. {
  232.     const int    WNE_TRAP_NUM = 0x60;
  233.     const int    UNIMPL_TRAP_NUM = 0x9f;
  234.     
  235.     macMode = TRUE;
  236.     
  237.     InitGraf(&qd.thePort);
  238.     InitFonts();
  239.     FlushEvents(everyEvent,0);
  240.     InitWindows();
  241.     InitMenus();
  242.     TEInit();
  243. //    InitDialogs((ResumeProcPtr)0);
  244.     InitDialogs((long)0);
  245.     InitCursor();
  246.     
  247.     wne_impl = (NGetTrapAddress(WNE_TRAP_NUM,ToolTrap) !=
  248.                 NGetTrapAddress(UNIMPL_TRAP_NUM,ToolTrap));
  249.  
  250.     dragRect  = qd.screenBits.bounds;
  251.     InsetRect(&dragRect, 4, 4);
  252.     
  253.     /* Setup the console */
  254.     SIOUXSettings.standalone = FALSE;
  255.     SIOUXSettings.setupmenus = FALSE;    /* This will fix your double menu problem */
  256.     SIOUXSettings.initializeTB = FALSE;    /* This is if YOU init the toolbox */
  257.     SIOUXSettings.autocloseonquit = TRUE;
  258.     SIOUXSettings.asktosaveonclose = TRUE;
  259.  
  260.     SIOUXHandleOneEvent(NULL);
  261. //    SIOUXDidEvent = SIOUXHandleOneEvent(&event);
  262.  
  263. }
  264.  
  265. /* DrawMyItem - user item routine for progress indicator of wait dialog */
  266. static void pascal DrawMyItem(WindowPtr theWindow, short itemNum)
  267. {
  268.     int                tmp;
  269.     Rect            rect;
  270.     static RGBColor    colors[] = { { 0xcccc, 0xcccc, 0xffff },        // Baby blue
  271.                                   { 0x4444, 0x4444, 0x4444 },        // DarkGrey
  272.                                  };
  273.     
  274.     /* copy the item rect */
  275.     rect = progressItemRect;
  276.     
  277.     /* draw a black frame */
  278.     FrameRect(&rect);
  279.     
  280.     /* draw the indicator background */
  281.     InsetRect(&rect,1,1);
  282.     tmp = rect.left;
  283.     rect.left += (int)((rect.right-rect.left) * progressItemValue / 100);
  284.     RGBBackColor(colors);
  285.     EraseRect(&rect);
  286.     
  287.     /* draw the indicated percentage */
  288.     RGBForeColor(colors + 1);
  289.     rect.right = rect.left;
  290.     rect.left = tmp;
  291.     PaintRect(&rect);
  292. }
  293.  
  294. /* ShowProgress - show progress dialog */
  295. /* (if percent<0 dialog is removed) */
  296. void ShowProgress(int percent, Boolean forceShow)
  297. {
  298.     short                itemType;
  299.     Handle                itemHand, dlogHandle;
  300.     long                tmpLong;
  301.     SignedByte            savedState;
  302.     static DialogPtr    progressDlg = 0;
  303.     const long            kProgressDlgID = 200L;
  304.     const long            kWaitTime = 10;
  305.     static long            nextTime = 0;
  306.     Rect                rect;
  307.     GrafPtr                oldPort;
  308.  
  309.     if (percent < 0) {
  310.     
  311.         /* dispose the dialog if it exists */
  312.         if (progressDlg) {
  313.         
  314.             DisposDialog(progressDlg);
  315.             progressDlg = 0;
  316.             
  317.             progressItemValue = -1;
  318.             nextTime = 0;
  319.         }
  320.         
  321.     } else {
  322.     
  323.         /* create the dialog if it doesn't exist */
  324.         if (!progressDlg) {
  325.         
  326.             /* must lock resource so it isn't purged in GetNewDialog (!!)    */
  327.             /* (if it is purged, the PositionDialog() call would be undone) */
  328.             dlogHandle = GetResource('DLOG', kProgressDlgID);
  329.             savedState = HGetState(dlogHandle);
  330.             HLock(dlogHandle);
  331.             
  332.             /* position dialog and get handle */
  333.             progressDlg = GetNewDialog(kProgressDlgID,NULL,(WindowPtr)-1L);
  334.             
  335.             /* restore the resource memory state */
  336.             HSetState(dlogHandle, savedState);
  337.             
  338.             /* set the drawing proc for the progress indicator item */
  339.             GetDItem(progressDlg,kProgressIndicatorItem,&itemType,&itemHand,&progressItemRect);
  340.             SetDItem(progressDlg,kProgressIndicatorItem,itemType,(Handle)DrawMyItem,&progressItemRect);
  341.         }
  342.         
  343.         if (forceShow || (progressItemValue!=percent && TickCount()>nextTime)) {
  344.         
  345.             /* set the progress value */
  346.             progressItemValue = percent;
  347.             
  348.             /* set the next time to update this indicator */
  349.             nextTime = TickCount() + kWaitTime;
  350.             
  351.             if (forceShow) {
  352.             
  353.                 /* draw the whole dialog (to update text too) */
  354.                 DrawDialog(progressDlg);
  355.                 
  356.             } else {
  357.             
  358.                 /* update the progress indicator only */
  359.                 GetPort(&oldPort);
  360.                 SetPort(progressDlg);
  361.                 
  362.                 /* invalidate the progress item */
  363.                 InvalRect(&progressItemRect);
  364.                 
  365.                 SetPort(oldPort);
  366.             }
  367.         }
  368.     }
  369. }
  370.  
  371. /* Quit - Quit program */
  372. static void Quit(unsigned char *str)
  373. {
  374.     unsigned char    *pt;
  375.     const int        kMsgAlertID = 201;
  376.     const int        kLineLen = 50;
  377.     
  378.     if (macMode) {
  379.         ShowProgress(-1,TRUE);
  380.         ParamText(str,"\p","\p","\p");
  381.         NoteAlert(kMsgAlertID,0);
  382.         ExitToShell();
  383.     } else {
  384.         ++str;
  385.         while (*pt) {
  386.             pt = (unsigned char *)strchr((char *)str,'\r');
  387.             if (!pt) pt = (unsigned char *)strchr((char *)str,'\0');
  388.             /* keep lines to 80 chars or less */
  389.             if (pt - str > kLineLen) {
  390.                 pt = str + kLineLen;
  391.                 while (pt>str && !isspace(*pt)) --pt;
  392.                 if (pt == str) pt = str + kLineLen;
  393.             }
  394.             printf("%.*s\n",pt-str,str);
  395.             if (*pt == '\r') printf("\n");
  396.             str = pt + 1;
  397.         }
  398.         exit(1);
  399.     }
  400. }
  401.  
  402. /* HandleEvents - process Mac events */
  403. void HandleEvents()
  404. {
  405.     EventRecord        theEvent;
  406.     short            thePart;
  407.     WindowPtr        whichWindow;
  408.     DialogPtr        theDialog;
  409.     Boolean            abort = FALSE;
  410.     
  411.     /* process mac events */
  412.     if (wne_impl) {
  413.         WaitNextEvent(everyEvent, &theEvent, 0L, 0L);
  414.     } else {
  415.         SystemTask();
  416.         GetNextEvent(everyEvent, &theEvent);
  417.     }
  418.     
  419.     /* does the user want to abort? */
  420.     if (theEvent.what==keyDown && theEvent.modifiers&cmdKey && (theEvent.message&0xff)=='.') {
  421.         abort = TRUE;
  422.     } else if (IsDialogEvent(&theEvent)) {
  423.         if (DialogSelect(&theEvent, &theDialog, &thePart)) {
  424.             if (thePart == kStopButtonItem) abort = TRUE;
  425.         }
  426.     } else if (theEvent.what == mouseDown) {
  427.         /* handle window drags */
  428.         thePart = FindWindow(theEvent.where, &whichWindow);
  429.  
  430.         if (thePart == inDrag) {
  431.             SelectWindow(whichWindow);
  432.             DragWindow(whichWindow, theEvent.where, &dragRect);
  433.         }
  434.     }
  435.     if (abort) {
  436.         /* must close net to avoid problems if    */
  437.         /* quitting in the middle of a DNS lookup */
  438.         CloseResolver();
  439.     
  440.         Quit("\pWebStat processing halted!\rOutput file not written.\0");
  441.     }
  442. }
  443.  
  444. /*------------------------------------------------------------------------------------------
  445. ** Other routines
  446. */
  447.  
  448. /* MemoryError - print memory error message and terminate program */
  449. static void MemoryError()
  450. {
  451.     Quit("\pNot enough memory!\rPlease increase the memory size for WebStat using the Get Info option in the File menu.\0");
  452. }
  453.  
  454. /* ReverseAddress - reverse an IP address string */
  455. /* - kills source string                         */
  456. /* - returns pointer to last field in addr         */
  457. static char *ReverseAddress(char *src, char *dst)
  458. {
  459.     char    *pt;
  460.     char    *last_field = 0;
  461.     
  462.     dst[0] = 0;
  463.     
  464.     while ((pt=strrchr(src,'.')) != 0) {
  465.         if (!last_field) last_field = pt + 1;
  466.         strcat(dst,pt+1);
  467.         strcat(dst,".");
  468.         *pt = 0;
  469.     }
  470.     strcat(dst,src);
  471.     
  472.     return(last_field);
  473. }
  474.  
  475. /* GetIPAddress - get numerical address from string */
  476. static long GetIPAddress(char *hostname)
  477. {
  478.     int        i;
  479.     long    addr = 0;
  480.     
  481.     for (i=3;;) {
  482.         addr |= (long)atoi(hostname) << 8*i;
  483.         if (--i < 0) break;
  484.         hostname = strchr(hostname,'.') + 1;
  485.         if (!hostname) return(0L);
  486.     }
  487.     return(addr);
  488. }
  489.  
  490. /* ConvTime - get struct tm from time and date strings        */
  491. /* Time and date are in the form "09:57:56" and "03/28/94"    */
  492. /* otherwise a zero is returned.                            */
  493. static time_t ConvTime(char *time, char *date, struct tm *tms)
  494. {
  495.     /* do a quick check on the time and date formats so we can be    */
  496.     /* resonably certain that we are looking at the correct strings    */
  497.     if (time[2]!=':' || date[2]!='/') return((time_t)0);
  498.     
  499.     tms->tm_sec = atoi(time+6);
  500.     tms->tm_min = atoi(time+3);
  501.     tms->tm_hour = atoi(time);
  502.     tms->tm_mday = atoi(date+3);
  503.     tms->tm_mon = atoi(date) - 1;
  504.     tms->tm_year = atoi(date+6);
  505.     tms->tm_isdst = 0;
  506.     
  507.     return(mktime(tms));
  508. }
  509.  
  510. /* ConvDate - convert date of form "03/28/94" to "Mar 28 1994" */
  511. static char *ConvDate(const char *date)
  512. {
  513.     static char        buff[100];
  514.     static char *    months = "JanFebMarAprMayJunJulAugSepOctNovDec";
  515.     
  516.     sprintf(buff,"%3.3s %2d %4d",
  517.             months+3*(atoi(date)-1),
  518.             atoi(date + 3),
  519.             atoi(date + 6) + 1900);
  520.     return(buff);
  521. }
  522.  
  523. /* ConvDateBack - convert date of form "Mar 28 1994" to "03/28/94" */
  524. static char *ConvDateBack(const char *date)
  525. {
  526.     static char        buff[100];
  527. //    static char *    months = "JanFebMarAprMayJunJulAugSepOctNovDec";
  528.     char *    month = NewPtr(5);
  529.     int i = 0;
  530.     
  531.     sprintf(month,"%3.3s", date);
  532.     if (!strcmp(month, "Jan")) i = 1;
  533.     if (!strcmp(month, "Feb")) i = 2;
  534.     if (!strcmp(month, "Mar")) i = 3;
  535.     if (!strcmp(month, "Apr")) i = 4;
  536.     if (!strcmp(month, "May")) i = 5;
  537.     if (!strcmp(month, "Jun")) i = 6;
  538.     if (!strcmp(month, "Jul")) i = 7;
  539.     if (!strcmp(month, "Aug")) i = 8;
  540.     if (!strcmp(month, "Sep")) i = 9;
  541.     if (!strcmp(month, "Oct")) i = 10;
  542.     if (!strcmp(month, "Nov")) i = 11;
  543.     if (!strcmp(month, "Dec")) i = 12;
  544.     sprintf(buff,"%2d/%2d/%2d",
  545.             i,
  546.             atoi(date + 3),
  547.             atoi(date + 9));
  548.     return(buff);
  549. }
  550.  
  551. /* GetDateStr - get date string from ctime() output */
  552. static void GetDateStr(char *out, char *ctimeStr)
  553. {
  554.     memcpy(out, ctimeStr+4, 7);            // copy month and day
  555.     memcpy(out+7, ctimeStr+20, 4);        // copy year
  556.     out[11] = 0;                        // null terminate it
  557. }
  558.  
  559. /* GetNumDays - get the number of days between two time_t values */
  560. static long GetNumDays(time_t start, time_t end)
  561. {
  562.     struct tm    *tms;
  563.     int            day1, year1;
  564.     int            day2, year2;
  565.     long        numDays;
  566.     
  567.     if (start >= end) return(1);        // exit gracefully on bad input
  568.     
  569.     tms   = localtime(&start);
  570.     day1  = tms->tm_yday;
  571.     year1 = tms->tm_year;
  572.     tms   = localtime(&end);
  573.     day2  = tms->tm_yday;
  574.     year2 = tms->tm_year;
  575.     
  576.     numDays = day2 - day1;
  577.     
  578.     while (year1 < year2) {
  579.         if (year1 % 4 != 0) numDays += 365L;
  580.         else if (year1 % 100 != 0) numDays += 366L;    // leap year
  581.             else numDays += 365L;    // 365 days at turn of century
  582.         ++year1;
  583.     }
  584.     if (numDays < 1) numDays = 1;
  585.     
  586.     return(numDays);
  587. }
  588.     
  589. /*-----------------------------------------------------------------------------------------
  590. ** Comparison routines
  591. */
  592.  
  593. /* CompStat - compare two statistics string names (used in qsort()) */
  594. int CompStat(const void *p1, const void *p2)
  595. {
  596.     char    *str1 = ((Statistic *)p1)->name;
  597.     char    *str2 = ((Statistic *)p2)->name;
  598.  
  599.     return(strcmpi(str1,str2));
  600. }
  601. /* CompStatNum - compare two statistics string names, evaluating numerical addresses */
  602. int CompStatNum(const void *p1, const void *p2)
  603. {
  604.     int        n1, n2;
  605.     char    *str1 = ((Statistic *)p1)->name;
  606.     char    *str2 = ((Statistic *)p2)->name;
  607.  
  608.     if (isdigit(*str1) && isdigit(*str2)) {
  609.     
  610.         for (;;) {
  611.             n1 = atoi(str1);
  612.             n2 = atoi(str2);
  613.             if (n1 == n2) {
  614.                 str1 = strchr(str1,'.');
  615.                 if (!str1) return(0);
  616.                 str2 = strchr(str2,'.');
  617.                 if (!str2) return(0);
  618.                 ++str1;
  619.                 ++str2;
  620.             } else if (n1 < n2) {
  621.                 return(-1);
  622.             } else {
  623.                 return(1);
  624.             }
  625.         }
  626.  
  627.     } else {
  628.     
  629.         return(strcmpi(str1,str2));
  630.     }
  631. }
  632.  
  633. /*--------------------------------------------------------------------------------------
  634. ** StringLookup class methods
  635. */
  636. /* StringLookup - class constructor */
  637. StringLookup::StringLookup(long incr, Boolean part)
  638.              : num(0),maxNum(0),incrNum(incr),partialCompare(part)
  639. {
  640. }
  641.  
  642. /* ~StringLookup - class destructor */
  643. StringLookup::~StringLookup()
  644. {
  645.     long        i;
  646.     
  647.     /* reset name list */
  648.     if (maxNum) {
  649.         for (i=0; i<num; ++i) delete list[i];
  650.         maxNum = num = 0;
  651.         delete list;
  652.     }
  653. }
  654.  
  655. /* AddString - add string to lookup list */
  656. void StringLookup::Add(char *code, char *name)
  657. {
  658.     int        i;
  659.     char    **oldList;
  660.     
  661.     /* expand country list if necessary */
  662.     if (num >= maxNum) {
  663.         oldList = list;
  664.         list = new char *[maxNum + incrNum];
  665.         if (!list) MemoryError();
  666.  
  667.         if (maxNum) {
  668.             memcpy(list, oldList, maxNum * sizeof(char *));
  669.             delete oldList;
  670.         }
  671.         maxNum += incrNum;
  672.     }
  673.     
  674.     /* add country to list */
  675.     i = strlen(code) + strlen(name) + 2;
  676.     list[num] = new char[i];
  677.     if (!list[num]) MemoryError();
  678.     strcpy(list[num],code);
  679.     strcpy(strchr(list[num],'\0')+1,name);
  680.     ++num;
  681. }
  682.  
  683. /* Lookup - look up an entry in the list */
  684. char *StringLookup::Lookup(char *code)
  685. {
  686.     long        i;
  687.     
  688.     /* search for entry in list */
  689.     for (i=0; i<num; ++i) {
  690.         if (partialCompare) {
  691.             if (!strcmpiPart(list[i], code))  return(strchr(list[i],'\0')+1);
  692.         } else {
  693.             if (!strcmpi(list[i], code))  return(strchr(list[i],'\0')+1);
  694.         }
  695.     }
  696.     
  697.     return((char *)0);
  698. }
  699.  
  700. /*-----------------------------------------------------------------------------------------
  701. ** Statistic class methods
  702. */
  703. /* Statistic - constructor */
  704. Statistic::Statistic()  : files(0), bytes(0), name(0), visits(0), that_time(0)
  705. {
  706.     /* empty method */
  707. }
  708.  
  709. /* ~Statistic - destructor */
  710. Statistic::~Statistic()
  711. {
  712.     if (name) delete name;        // free memory for name
  713. }
  714.  
  715. /* IncrStats - increment statistics counters */
  716. void Statistic::IncrStats(double byte_count)
  717. {
  718.     files++;
  719.     bytes += byte_count;
  720. }
  721.  
  722. /* NewVisit - increment visit count if need be */
  723. Boolean Statistic::NewVisit(time_t this_time)
  724. {
  725.     Boolean aVisit = FALSE;
  726.     
  727.     if (that_time) {
  728.         if ((this_time < that_time) || (difftime(this_time, that_time) > (kVisitPause*60))) {
  729.         // I think difftime returns seconds - hence kVisitPause*60 = kVisitPause minutes
  730.             visits++;
  731.             aVisit = TRUE;
  732.         }
  733.     } else {
  734.         visits++;
  735.         aVisit = TRUE;
  736.     }
  737.     that_time = this_time;
  738.     
  739.     return aVisit;
  740. }
  741.  
  742. /* IncrVisits - increment statistics counters */
  743. void Statistic::IncrVisits(double a_one)
  744. {
  745.     visits++;
  746. }
  747.  
  748. /*-----------------------------------------------------------------------------------------
  749. ** StatList class methods
  750. */
  751. /* StatList - constructor */
  752. StatList::StatList(long iSize) : num(0)
  753. {
  754.     incrSize = maxNum = iSize;
  755.     
  756.     stat = new Statistic[iSize];
  757.     
  758.     if (!stat) MemoryError();
  759. }
  760.  
  761. /* ~StatList - destructor */
  762. StatList::~StatList()
  763. {
  764.     /* delete all Statistic objects */
  765.     for (long n=0; n<maxNum; ++n) delete(stat+n);
  766. }
  767.  
  768. /* Write - write list to file */
  769. void StatList::Write(FILE *fp, char *heading)
  770. {
  771.     int            i,len;
  772.     long        n;
  773.     double        totFile, totByte;
  774.     const int    BSIZ = 256;
  775.     char        *name, buff[BSIZ];
  776.     Boolean        anyVisits = FALSE;
  777.     
  778.     for (n=0; n<num; ++n) {
  779.         if (stat[n].visits) anyVisits = TRUE;
  780.     }
  781.     len = strlen(heading);
  782.     
  783.     if (len > BSIZ) return;
  784.     
  785.     memset(buff,'-',len);
  786.     buff[len] = 0;
  787.     
  788.     if (!totalFiles) totFile = 1;                // avoid divide by zero
  789.     else             totFile = totalFiles / 100.0;
  790.     
  791.     if (!totalBytes) totByte = 1;
  792.     else             totByte = totalBytes / 100.0;
  793.     
  794.     fprintf(fp,"<pre>\n");
  795. //    fprintf(fp,"%*s  Number of    Number of   Percent of  Percent of  Number of\n",len,"");
  796. //    fprintf(fp, "%s  Files Sent   Bytes Sent  Files Sent  Bytes Sent     Visits\n",heading);
  797. //    fprintf(fp, "%s  ----------  -----------  ----------  ----------  ---------\n",buff);
  798.     if (anyVisits) {
  799.         fprintf(fp,"%*s   Files  Visits      Bytes      Percentage of\n",len,"");
  800.         fprintf(fp, "%s    Sent    Made       Sent     Files    Bytes\n",heading);
  801.         fprintf(fp, "%s  ------  ------  ---------   -------  -------\n",buff);
  802. //                       123456  123456 1234567890    123.1     123.1
  803. //        fprintf(fp,"%-*s %6ld  %6ld %9.0f    %3.1f     %3.1f \n",
  804.     } else {
  805.         fprintf(fp,"%*s   Files              Bytes      Percentage of\n",len,"");
  806.         fprintf(fp, "%s    Sent               Sent     Files    Bytes\n",heading);
  807.         fprintf(fp, "%s  ------          ---------   -------  -------\n",buff);
  808.     }
  809.     
  810.     ++len;
  811.     
  812.     for (n=0; n<num; ++n) {
  813.     
  814.         if ((i=strlen(stat[n].name))<=len || len<3) {
  815.             name = stat[n].name;
  816.         } else {
  817.             strcpy(buff,"...");
  818.             strcat(buff,stat[n].name+i-len+3);
  819.             name = buff;
  820.         }
  821.         
  822. //        if (stat[n].visits) {
  823. //            fprintf(fp, "%10ld ", stat[n].visits);
  824. //        }
  825.         if (anyVisits) {
  826.             fprintf(fp,"%-*s %6ld  %6ld %10.0f    %6.2f   %6.2f \n",
  827.                     len,
  828.                     name,
  829.                     stat[n].files,
  830.                     stat[n].visits,
  831.                     stat[n].bytes,
  832.                     stat[n].files / totFile,
  833.                     stat[n].bytes / totByte);
  834.         } else {
  835.             fprintf(fp,"%-*s %6ld         %10.0f    %6.2f   %6.2f \n",
  836.                     len,
  837.                     name,
  838.                     stat[n].files,
  839.                     stat[n].bytes,
  840.                     stat[n].files / totFile,
  841.                     stat[n].bytes / totByte);
  842.         }
  843. //        fprintf(fp,"%-*s %10ld%13.0f    %6.2f      %6.2f   %10ld\n",
  844. //                len,
  845. //                name,
  846. //                stat[n].files,
  847. //                stat[n].bytes,
  848. //                stat[n].files / totFile,
  849. //                stat[n].bytes / totByte,
  850. //                stat[n].visits);
  851.     }
  852.     fprintf(fp,"</pre><p>\n");
  853. }
  854.  
  855. /* SearchStat - search list for matching string, creating new entry if not found */
  856. long StatList::SearchStat(char *str)
  857. {
  858.     long        n;
  859.     Statistic    *tempStat;
  860.     
  861.     /* search through stat names (most recent first for speed) */
  862.     for (n=num-1; n>=0; --n) {
  863.         if (!strcmpi(str,stat[n].name)) return(n);
  864.     }
  865.     if (num >= maxNum) {
  866.         tempStat = new Statistic[maxNum + incrSize];
  867.         if (!tempStat) MemoryError();
  868.         for (n=0; n<maxNum; ++n) {
  869.             tempStat[n] = stat[n];
  870.             stat[n].name = 0;        // make sure memory is not deallocated for name
  871.         }
  872.         delete stat;
  873.         stat = tempStat;
  874.         maxNum += incrSize;
  875.     }
  876.     stat[num].name = new char[strlen(str)+1];
  877.     if (!stat[num].name) MemoryError();
  878.     strcpy(stat[num].name,str);
  879.     return(num++);
  880. }
  881.  
  882. /* IncrStats - increment the stats for given string */
  883. long StatList::IncrStats(double byte_count, char *str)
  884. {
  885.     long    n;
  886.     
  887.     n = SearchStat(str);
  888.     
  889.     stat[n].IncrStats(byte_count);    // increment individual sums
  890.  
  891.     return n;
  892. }
  893.  
  894. /* CalcVisit - Keep take of visits by/to each string */
  895. Boolean StatList::CalcVisit(time_t this_time, char *str)
  896. {
  897.     long    n;
  898.     Boolean aVisit = FALSE;
  899.     
  900.     n = SearchStat(str);
  901.     
  902.     if (stat[n].NewVisit(this_time)) {    // possible new visit
  903.         aVisit = TRUE;
  904.         StatList::totalVisits++;
  905.         if (n > StatList::totalSites) StatList::totalSites = n;
  906.     }
  907.  
  908.     return aVisit;
  909. }
  910.  
  911. /* aVisit - tell the other categories about the new visit */
  912. void StatList::aVisit(char *str)
  913. {
  914.     long    n;
  915.     
  916.     n = SearchStat(str);
  917.     
  918.     stat[n].IncrVisits(1);
  919. }
  920.  
  921. /* IncrTotals - increment total counters */
  922. void StatList::IncrTotals(double byte_count)
  923. {
  924.     totalFiles += 1;                    // increment totals
  925.     totalBytes += byte_count;
  926. }
  927.  
  928. /* Substitute - substitute names in list */
  929. void StatList::Substitute(char *(*func)(const char *))
  930. {
  931.     char    *pt, *pt2;
  932.     
  933.     for (long n=0; n<num; ++n) {
  934.         pt = func(stat[n].name);        // get new string
  935.         if (pt) {
  936.             pt2 = new char[strlen(pt) + 1];
  937.             if (!pt2) MemoryError();
  938.             strcpy(pt2,pt);                // copy the string
  939.             delete stat[n].name;        // get rid of old string
  940.             stat[n].name = pt2;
  941.         }
  942.     }
  943. }
  944.  
  945. /* Sort - sort the list alphabetically */
  946. void StatList::Sort(CompFunc func)
  947. {
  948.     if (!func) func = CompStat;
  949.     
  950.     qsort(stat, num, sizeof(Statistic), func);
  951. }
  952.  
  953. /* Summarize - print summary of total day statistics */
  954. void StatList::Summarize(FILE *fp, time_t first, time_t last)
  955. {
  956.     char    firstStr[30];
  957.     char    lastStr[30];
  958.     long    numDays;
  959.     
  960.     numDays = GetNumDays(first, last);
  961.     
  962.     GetDateStr(firstStr, ctime(&first));
  963.     GetDateStr(lastStr,  ctime(&last));
  964.     
  965.     fprintf(fp,"<h2>Summary for Period: %s to %s</h2>\n", firstStr, lastStr);
  966.     fprintf(fp,"<h3>Duration of Summary: %2.0f days</h3>\n", (float)numDays);
  967.     fprintf(fp,"<pre>\n");
  968.     totalSites++;
  969.     fprintf(fp,"Total number of distinct Server Visits   %14.0f\n",totalVisits);
  970.     fprintf(fp,"Total number of distinct Sites Visiting  %14.0f\n",totalSites);
  971.     fprintf(fp,"\n");
  972.     fprintf(fp,"Average number of Visits Daily           %14.0f\n",totalVisits/numDays);
  973.     fprintf(fp,"Average Files Transmitted Daily          %14.0f\n",totalFiles/numDays);
  974.     fprintf(fp,"Average Bytes Transmitted Daily          %14.0f\n",totalBytes/numDays);
  975.     fprintf(fp,"\n");
  976.     fprintf(fp,"Files Transmitted During Summary Period  %14.0f\n",totalFiles);
  977.     fprintf(fp,"Bytes Transmitted During Summary Period  %14.0f\n",totalBytes);
  978.     fprintf(fp,"</pre><p>\n");
  979. }
  980.  
  981. /*-----------------------------------------------------------------------------------------
  982. ** main program
  983. */
  984. void main()
  985. {
  986.     unsigned char pirate[10];
  987.  
  988.     long            i, line, fileSize, os1, os2;
  989.     long            numExcl = 0, maxExcl = 0;
  990.     long            numCtry = 0;
  991.     char            *logFile = def_log_file;
  992.     char            *outFile = def_out_file;
  993.     char            *fmtFile = def_fmt_file;
  994.     Boolean            messages = FALSE;
  995.     Boolean            long_addr = TRUE;
  996.     Boolean            dnsLookup = FALSE;
  997.     Boolean            isReverse;
  998.     time_t            tt, firstTime, lastTime;
  999.     struct tm        tms;
  1000.     char            *pt,*pt2, *pt3, *country, *last_field;
  1001.     char            *domain, *stat_str, *div_str = NewPtr(31);
  1002.     char            *file = NewPtr(50), *time_str = NewPtr(25), *date_str = NewPtr(25);
  1003.     char            *filetemp = NewPtr(50);
  1004.     char            **exclude, **oldExcl;
  1005.     const int        BSIZ = 512;
  1006.     Str255            curVol;
  1007.     short            vRefNum;
  1008.     char            rev_domain[BSIZ],buff[BSIZ],dns_name[BSIZ],buf2[BSIZ];
  1009.     StringLookup    *countryList, *addressList;
  1010.     double            bytes;
  1011.     StatList        *byDate, *byHour, *byWeekday, *byDomain, *bySubdomain, *byFile;
  1012.     FILE            *fp, *out, *fmt;
  1013.     static char        *syntaxFmt = "Syntax error in %s line %ld: %s '%s'\n";
  1014.     static char        *delim = "  []\"\t\n";
  1015.     static char        *delimCntry = "  []\t\n";
  1016. //cantso.canterbury.ac.nz - - [10/Nov/1994:14:21:41 +-1400] "GET /home.htm HTTP/1.0" 200 2941
  1017.     static char        *weekdays[] = { "Sunday","Monday","Tuesday","Wednesday",
  1018.                                     "Thursday","Friday","Saturday" };
  1019.  
  1020.     ToolBoxInit();
  1021.  
  1022.     if (!date_str) date_str = NewPtr(15);
  1023.     if (!time_str) time_str = NewPtr(15);
  1024.     if (!pirate[1]) pirate[1] = 2;
  1025.  
  1026.     strcpy(div_str, "1234"); // a quick hack to init div_str
  1027.  
  1028.     firstTime = 0;
  1029.     
  1030.     /* initialize country list object */
  1031.     countryList = new StringLookup(kCountryIncr,TRUE);
  1032.     if (!countryList) MemoryError();
  1033.     
  1034.     /* read config file */
  1035.     if ((fp = fopen(config_file,"r")) == 0) {
  1036.         printf("Config file %s not found!\nUsing defaults.\n",config_file);
  1037.     } else {
  1038.         for (line=1; fgets(buff,BSIZ,fp); ++line) {
  1039.  
  1040.             pt = strtokQuote(buff,delim);
  1041.             if (!pt || *pt=='#') continue;
  1042.             
  1043.             pt2 = strtokQuote(NULL,delim);
  1044.             if (!pt2) {
  1045.                 printf(syntaxFmt,config_file,line,"No parameters for",pt);
  1046.                 continue;
  1047.             }
  1048.             
  1049.             if (!strcmpi(pt,"LOG")) {
  1050.                 logFile = new char[strlen(pt2) + 1];
  1051.                 if (!logFile) MemoryError();
  1052.                 strcpy(logFile, pt2);
  1053.             
  1054.             } else if (!strcmpi(pt,"OUTPUT")) {
  1055.                 outFile = new char[strlen(pt2) + 1];
  1056.                 if (!outFile) MemoryError();
  1057.                 strcpy(outFile, pt2);
  1058.             
  1059.             } else if (!strcmpi(pt,"FORMAT")) {
  1060.                 fmtFile = new char[strlen(pt2) + 1];
  1061.                 if (!fmtFile) MemoryError();
  1062.                 strcpy(fmtFile, pt2);
  1063.                 
  1064.             } else if (!strcmpi(pt,"EXCLUDE")) {
  1065.                 if (numExcl >= maxExcl) {
  1066.                     oldExcl = exclude;
  1067.                     exclude = new char *[maxExcl + kExcludeIncr];
  1068.                     if (!exclude) {
  1069.                         printf("Too many exclusions\n");
  1070.                         MemoryError();
  1071.                     }
  1072.                     if (maxExcl) {
  1073.                         memcpy(exclude, oldExcl, maxExcl * sizeof(char *));
  1074.                         delete oldExcl;
  1075.                     }
  1076.                     maxExcl += kExcludeIncr;
  1077.                 }
  1078.                 exclude[numExcl] = new char[strlen(pt2) + 1];
  1079.                 if (!exclude[numExcl]) MemoryError();
  1080.                 strcpy(exclude[numExcl], pt2);
  1081.                 ++numExcl;
  1082.             
  1083.             } else if (!strcmpi(pt,"MESSAGES")) {
  1084.                 if (!strcmpi(pt2,"On")) messages = TRUE;
  1085.                 
  1086.             } else if (!strcmpi(pt,"ADDRESSES")) {
  1087.                 if (!strcmpi(pt2,"Short")) long_addr = FALSE;
  1088.                 
  1089.             } else if (!strcmpi(pt,"DOMAIN")) {
  1090.                 pt3 = strchr(pt2,'*');
  1091.                 if (pt3) *pt3 = 0;            // get rid of '*' if it exists
  1092.                 pt3 =  pt2;
  1093.                 while(*pt3) {
  1094.                     if ((*pt3 < ' ') || (*pt3 > 'z'))
  1095.                         *pt3 = 0;    // get rid of space if it exists
  1096.                     else
  1097.                         pt3++;
  1098.                 }
  1099.                 pt3 = strtokQuote(NULL,delimCntry);
  1100.                 if (!pt3) {
  1101.                     printf(syntaxFmt,config_file,line,"Too few parameters for",pt);
  1102.                     continue;
  1103.                 }
  1104.                 countryList->Add(pt2, pt3);
  1105.                 ++numCtry;
  1106.  
  1107.             } else if (!strcmpi(pt,"DNSLOOKUP")) {
  1108.                 if (!strcmpi(pt2,"On")) dnsLookup = TRUE;
  1109.                 
  1110.             } else {
  1111.                 printf(syntaxFmt,config_file,line,"Unknown keyword",pt);
  1112.             }
  1113.         }
  1114.         fclose(fp);
  1115.     }
  1116.     
  1117.     if (messages) {
  1118.         printf("\n*****************************\n");
  1119.         printf("** - %s - **\n",program_name);
  1120.         printf("**  A utility for MacHTTP  **\n");
  1121.         printf("**     by Phil Harvey      **\n");
  1122.         printf("**  Mods by Denis Birnie   **\n");
  1123.         printf("*****************************\n\n");
  1124.         if (numCtry) printf("%ld domain name%s registered.\n",numCtry,numCtry==1?"":"s");
  1125.         if (numExcl) printf("%ld exclusion%s registered.\n",numExcl,numExcl==1?"":"s");
  1126.         if (numCtry || numExcl) printf("\n");
  1127.     }
  1128.     
  1129.     /* open log file */
  1130.     if ((fp = fopen(logFile,"r")) == 0) {
  1131.         printf("Can't open log file %s !\n\n",logFile);
  1132.         printf("The log file name can be specified in WebStat.config.\n");
  1133.         printf("If no path is specified, then the log file\n");
  1134.         printf("must be in the same folder as the WebStat program.\n");
  1135.         exit(1);
  1136.     }
  1137.     
  1138.     /* open format file */
  1139.     if ((fmt = fopen(fmtFile,"r")) == 0) {
  1140.         printf("Can't open the HTML format file %s !\n\n",fmtFile);
  1141.         printf("If no path is specified, then the format file\n");
  1142.         printf("must be in the same folder as the WebStat program.\n");
  1143.         exit(1);
  1144.     }
  1145.     
  1146.     if (!messages) {
  1147. //        ToolBoxInit();
  1148.         ParamText("\pProcessing log file","\p","\p","\p");
  1149.         ShowProgress(0,TRUE);
  1150.     }
  1151.     
  1152.     /* initialize lists */
  1153.     byDate        = new StatList(kDateIncr);
  1154.     byHour         = new StatList(kHourIncr);
  1155.     byWeekday    = new StatList(kWDayIncr);
  1156.     byDomain    = new StatList(kDomainIncr);
  1157.     bySubdomain = new StatList(kSubDomainIncr);
  1158.     byFile        = new StatList(kFileIncr);
  1159.     
  1160.     /* initialize weekday and hour names */
  1161.     for (i=0; i<7; ++i) byWeekday->SearchStat(weekdays[i]);
  1162.     for (i=0; i<24; ++i) {
  1163.         sprintf(buf2," %.2ld",i);
  1164.         byHour->SearchStat(buf2);
  1165.     }
  1166.  
  1167.     /* init MacTCP if doing DNS lookups */
  1168.     if (dnsLookup) {
  1169.     
  1170.         /* get current volume (DNS routines change this!) */
  1171.         GetVol(curVol,&vRefNum);
  1172.         
  1173.         /* initialize DNS lookup cache table */
  1174.         addressList = new StringLookup(kDNSListIncr,FALSE);
  1175.         if (!addressList) MemoryError();
  1176.         if (messages) printf("Initializing MacTCP...\n");
  1177.         if (InitNetwork() != noErr) {
  1178.             Quit("\pNet init error!\rYou may want to set DNSLOOKUP Off in your WebStat.config file.\0");
  1179.         }
  1180.     }
  1181.         
  1182.     /* read the log file */
  1183.     if (messages) {
  1184.         printf("Reading file %s...\n",logFile);
  1185.     } else {
  1186.         /* get size of file */
  1187.         fseek(fp,0L,SEEK_END);
  1188.         fileSize = ftell(fp);
  1189.         if (!fileSize) ++fileSize;        // prevent /0 errors
  1190.         fseek(fp,0L,SEEK_SET);
  1191.     }
  1192.     
  1193.     while (fgets(buff,BSIZ,fp)) {
  1194.     
  1195.         if (!messages) {
  1196.             
  1197.             /* handle events */
  1198.             HandleEvents();
  1199.             
  1200.             /* update the progress indicator */
  1201.             ShowProgress(ftell(fp)*100L/fileSize, FALSE);
  1202.         }
  1203.         
  1204. // These are the original log file inputs - MacHttp format?
  1205. //        date_str = strtok(buff,delim);
  1206. //        time_str = strtok(NULL,delim);
  1207. //        stat_str = strtok(NULL,delim);
  1208. //        domain   = strtok(NULL,delim);
  1209. //        file     = strtok(NULL,delim);
  1210. //        bytes     = atof(strtok(NULL,delim));
  1211.  
  1212. // These are Den's inputs - WinHttp format.
  1213. //132.181.30.25 - - [21/Dec/1995:11:31:34 +1400] "GET /Home.html HTTP/1.0" 200 4540
  1214.         domain   = strtok(buff,delim);
  1215.         strtok(NULL,delim);
  1216.         strtok(NULL,delim);
  1217.         div_str = strtok(NULL,delim);
  1218.         sprintf(date_str,"%3.3s %2.2s %4.4s",
  1219.             div_str+3,
  1220.             div_str,
  1221.             div_str+7);
  1222.         sprintf(time_str,"%8.8s", div_str+12);
  1223.         strtok(NULL,delim);
  1224.         strtok(NULL,delim);
  1225.         div_str = strtok(NULL,delim);
  1226.         if (*(div_str+1) > 0)
  1227.             sprintf(file,"%s", div_str+1);
  1228.         else
  1229.             sprintf(file,"/");
  1230.         stat_str = strtok(NULL,delim);
  1231.         if (memcmp(stat_str,"200",3)) stat_str = strtok(NULL,delim);
  1232.         bytes     = atof(strtok(NULL,delim));
  1233.  
  1234.         /* ignore lines that don't have enough entries */
  1235. //        printf("** file, Stat_str -> %s, %s\n",file, stat_str);
  1236. //        if (!file) continue;
  1237. //        if (!memcmp(file,"\0",1)) continue;
  1238.         
  1239.         date_str = ConvDateBack(date_str);
  1240.  
  1241.         /* convert the time and date strings */
  1242.         tt = ConvTime(time_str, date_str, &tms);
  1243.         
  1244.         /* ignore line if the date didn't convert properly */
  1245.         if (!tt) continue;
  1246.         
  1247.         /* save first and last times */
  1248.         if (!firstTime) firstTime = tt;
  1249.         
  1250.         /* clean up address string and determine if it is reversed */
  1251.         isReverse = TRUE;
  1252.         if (isdigit(domain[0])) {
  1253.             pt = strstr(domain,".in-addr");        // get rid of ".in-addr.arpa."
  1254.             if (pt) *pt = 0;
  1255.             else isReverse = FALSE;                // NO_DNS option must be on (name not rev)
  1256. //        } else {
  1257. //            domain[strlen(domain)-1] = 0;        // get rid of trailing '.'
  1258.         }
  1259.         
  1260.         /* reverse the subdomain if necessary */
  1261.         if (isReverse) {
  1262.             last_field = ReverseAddress(domain,rev_domain);
  1263.         } else {
  1264.             strcpy(rev_domain,domain);
  1265.         }
  1266.         
  1267.         /* resolve all numerical addresses if DNSLOOKUP is On */
  1268.         if (dnsLookup && isdigit(rev_domain[0])) {
  1269.         
  1270.             /* does this entry exist in address cache? */
  1271.             pt = addressList->Lookup(rev_domain);
  1272.             
  1273.             /* if not found, consult DNS for translation */
  1274.             if (!pt) {
  1275.                 
  1276.                 if (messages) printf("DNS lookup %s -> ",rev_domain);
  1277.  
  1278.                 ParamText("\pLooking up Domain Name","\p","\p","\p");
  1279.                 ShowProgress(ftell(fp)*100L/fileSize, TRUE);
  1280.  
  1281.                 /* get resolved address in buf2 */
  1282.                 if (IPAddrToName(GetIPAddress(rev_domain),dns_name) == noErr) {
  1283.                 
  1284.                     dns_name[strlen(dns_name)-1] = 0;        // get rid of trailing '.'
  1285.  
  1286.                     if (messages) printf("%s\n",dns_name);
  1287.                     
  1288.                     /* must reverse the name returned by DNS */
  1289.                     last_field = ReverseAddress(dns_name, buf2);
  1290.                     
  1291.                     /* add to name cache */
  1292.                     addressList->Add(rev_domain, buf2);
  1293.                     
  1294.                     /* copy new name into domain string */
  1295.                     strcpy(rev_domain, buf2);
  1296.                 
  1297.                 } else {
  1298.                 
  1299.                     /* use unresolved name if host unknown */
  1300.                     addressList->Add(rev_domain, rev_domain);
  1301.                     
  1302.                     if (messages) printf("<unknown>\n");
  1303.                 }
  1304.  
  1305.                 ParamText("\pProcessing log file","\p","\p","\p");
  1306.                 ShowProgress(ftell(fp)*100L/fileSize, TRUE);
  1307.                 
  1308.             } else {
  1309.             
  1310.                 /* use the converted name from the cache */
  1311.                 strcpy(rev_domain, pt);
  1312.             }
  1313.         }
  1314.         
  1315.         /* check for exclusions */
  1316.         for (i=0; i<numExcl; ++i) {
  1317.             if (!strcmpiWild(exclude[i],rev_domain)) break;
  1318.         }
  1319.         if (i != numExcl) continue;
  1320.         
  1321.         /* parse filespec */
  1322.         for (pt=file; *pt; ++pt) {
  1323.             if (*pt==':') {
  1324.                 if (pt[1]) *pt = '/';        // convert ':' to '/'
  1325.                 else *pt = 0;
  1326.             } else if (*pt=='?' || *pt=='$') {
  1327.                 *pt = 0;                    // truncate file names at '?' or '$'
  1328.                 break;
  1329.             }
  1330.         }
  1331.             
  1332.         /* increment totals */
  1333.         StatList::IncrTotals(bytes);
  1334.         
  1335.         /* weekly totals */
  1336.         byWeekday->IncrStats(bytes, weekdays[tms.tm_wday]);
  1337.         
  1338.         /* hourly totals */
  1339.         sprintf(buf2," %.2d",tms.tm_hour);
  1340.         byHour->IncrStats(bytes, buf2);
  1341.         
  1342.         /* daily totals */
  1343.         byDate->IncrStats(bytes, date_str);
  1344.         
  1345.         /* totals by domain */
  1346.         country = countryList->Lookup(rev_domain);
  1347.  
  1348.         if (!country) {
  1349.             if (isdigit(rev_domain[0])) country = "[unresolved]";
  1350.             else country = last_field;
  1351.         }
  1352.         byDomain->IncrStats(bytes, country);
  1353.         
  1354.         /* shorten address if requested for subdomain list */
  1355.         if (!long_addr) {
  1356.             pt = strrchr(rev_domain,'.');
  1357.             if (pt) *pt = 0;
  1358.         }
  1359.  
  1360.         /* totals by subdomain */
  1361.         bySubdomain->IncrStats(bytes, rev_domain);
  1362.         if (bySubdomain->CalcVisit(tt, rev_domain)) {
  1363.             byWeekday->aVisit(weekdays[tms.tm_wday]);
  1364.             byHour->aVisit(buf2);
  1365.             byDate->aVisit(date_str);
  1366.             byDomain->aVisit(country);
  1367.         }
  1368.  
  1369.         /* totals by file */
  1370.         if (!memcmp(stat_str,"200",3)) {
  1371. //        if (((stat_str[0] == '2') && (stat_str[1] == '0') && (stat_str[2] == '0'))) {
  1372.             pt = "Home page [by default]";
  1373. //            if ((int)file) pt = file;
  1374.             if ((memcmp(file,"\0",1)) && (*(div_str+1) > 0)) pt = file;
  1375.         } else {
  1376.             if (messages) printf("All not well -> %s\n",stat_str);
  1377.             if (!memcmp(stat_str,"403",3)) {
  1378.                 pt = "* [Secured files]";
  1379.                 if (memcmp(file,"\0",1) && (*(div_str+1) > 0)) {
  1380.                     sprintf(filetemp,"* %s", file);
  1381.                     pt = filetemp;
  1382.                 }
  1383.             } else {
  1384.                 if (!memcmp(stat_str,"404",3)) {
  1385.                     pt = "? [nonexistent files]";
  1386.                     if (memcmp(file,"\0",1) && (*(div_str+1) > 0)) {
  1387.                         sprintf(filetemp,"? %s", file);
  1388.                         pt = filetemp;
  1389.                     }
  1390.                 } else {
  1391.                     pt = "! [Communication Fault]";
  1392.                     if (memcmp(file,"\0",1) && (*(div_str+1) > 0)) {
  1393.                         sprintf(filetemp,"! %s", file);
  1394.                         pt = filetemp;
  1395.                     }
  1396.                 }
  1397.             }
  1398.         }
  1399.  
  1400.         byFile->IncrStats(bytes, pt);
  1401.     
  1402.         lastTime = tt;
  1403.     }
  1404.     
  1405.     /* close log file */
  1406.     fclose(fp);
  1407.     
  1408.     /* convert date formats */
  1409.     byDate->Substitute(ConvDate);
  1410.     
  1411.     /* sort the stuff */
  1412.     byDomain->Sort();
  1413.     bySubdomain->Sort(CompStatNum);
  1414.     byFile->Sort();
  1415.     
  1416.     /* print results */
  1417.     if (messages) {
  1418.         printf("Writing file %s...\n",outFile);
  1419.     } else {
  1420.         ParamText("\pWriting output file","\p","\p","\p");
  1421.         ShowProgress(100,TRUE);
  1422.     }
  1423.     
  1424.     /* reset current volume if necessary */
  1425.     if (dnsLookup) SetVol(curVol,vRefNum);
  1426.     
  1427.     /* open output file */
  1428.     if ((out = fopen(outFile,"w")) == 0) {
  1429.         Quit("\pCan't open output file!\rAre your sure the output folder exists, and is not locked?\0");
  1430.     }
  1431.     
  1432.     while (fgets(buff, BSIZ, fmt)) {
  1433.     
  1434.         /* interpret special codes in format file */
  1435.         if (buff[0] == '[') {
  1436.             if (!strcmpiPart("[Summary]",buff)) {
  1437.                 time(&tt);
  1438.                 fprintf(out,"Generated by: %s<br>\n",program_name);
  1439.                 fprintf(out,"Last updated: %s<p>\n",ctime(&tt));
  1440.                 StatList::Summarize(out, firstTime, lastTime);
  1441.                 continue;
  1442.             } else if (!strcmpiPart("[Day]",buff)) {
  1443.                 byDate->Write(out,"   Date    ");
  1444.                 continue;
  1445.             } else if (!strcmpiPart("[Hour]",buff)) {
  1446.                 byHour->Write(out,"Time");
  1447.                 continue;
  1448.             } else if (!strcmpiPart("[Weekday]",buff)) {
  1449.                 byWeekday->Write(out,"    Day    ");
  1450.                 continue;
  1451.             } else if (!strcmpiPart("[Domain]",buff)) {
  1452.                 byDomain->Write(out,"        Domain Name        ");
  1453.                 continue;
  1454.             } else if (!strcmpiPart("[Subdomain]",buff)) {
  1455.                 bySubdomain->Write(out,"    Reversed  Subdomain    ");
  1456.                 continue;
  1457.             } else if (!strcmpiPart("[Archive]",buff)) {
  1458.                 byFile->Write(out,"      Archive Section      ");
  1459.                 continue;
  1460.             }
  1461.         }
  1462.         
  1463.         /* no special code found, copy line to the output */
  1464.         fputs(buff,out);
  1465.     }
  1466.     fclose(out);
  1467.     fclose(fmt);
  1468.     
  1469.     /* free up memory */
  1470.     delete byDate;
  1471.     delete byHour;
  1472.     delete byWeekday;
  1473.     delete byDomain;
  1474.     delete bySubdomain;
  1475.     delete byFile;
  1476.     
  1477.     /* delete exclusions */
  1478.     if (numExcl) {
  1479.         for (i=0; i<numExcl; ++i) delete exclude[i];
  1480.         delete exclude;
  1481.     }
  1482.     
  1483.     /* free country list */
  1484.     delete countryList;
  1485.     
  1486.     /* free DNS cache if allocated */
  1487.     if (dnsLookup) delete addressList;
  1488.  
  1489.     if (messages) {
  1490.         printf("Done.\n");
  1491.     } else {
  1492.         ShowProgress(-1,TRUE);
  1493.     }
  1494. }